#include <stdio.h>

#include "astree.h"
#include "tacs.h"

extern table symbolsTable;

// PROTOTYPES
TAC* generateCode(node* node);
TAC* make_unop(int type, TAC* code0);
TAC* make_binop(int type, TAC* code0, TAC* code1);
TAC* make_if_then_else(TAC* code0, TAC* code1, TAC* code2);
TAC* make_loop(TAC* code0, TAC* code1);
TAC* make_fundef(TAC* code0, TAC* code1);

// IMPLEMENTATION

TAC* generateCode(node* node) {
	int i;
	TAC *code[MAX_SONS];
	TAC* result;

	if(node == NULL) {
		return 0;
	}

	// PROCESS CHILDREN FIRST RECURSIVELLY
	for(i=0; i<MAX_SONS; i++) {
		if(node->son[i]) {
			code[i] = generateCode(node->son[i]);
		}
		else {
			code[i] = NULL;
		}
	}

	switch(node->type) {
		// CASE FOR EACH ASTREE NODE TYPE
		case AST_IDENTIFIER:// identifier or literal???
		case AST_LITERAL:
			result = tac_create(TAC_SYMBOL, node->value, 0, 0); //node->symbol é ponteiro pra tabela
			break;
	
		case AST_OP_REF: result = make_unop(TAC_REF, code[0]); break;
		case AST_OP_DEREF: result = make_unop(TAC_DEREF, code[0]); break;
		case AST_OP_ADD: result = make_binop(TAC_ADD, code[0]), code[1]); break;
		case AST_OP_SUB: result = make_binop(TAC_SUB, code[0]), code[1]); break;
		case AST_OP_MUL: result = make_binop(TAC_MUL, code[0]), code[1]); break;
		case AST_OP_DIV: result = make_binop(TAC_DIV, code[0]), code[1]); break;
		case AST_OP_LE: result = make_binop(TAC_LE, code[0]), code[1]); break;
		case AST_OP_GE: result = make_binop(TAC_GE, code[0]), code[1]); break;
		case AST_OP_EQ: result = make_binop(TAC_EQ, code[0]), code[1]); break;
		case AST_OP_NE: result = make_binop(TAC_NE, code[0]), code[1]); break;
		case AST_OP_AND: result = make_binop(TAC_AND, code[0]), code[1]); break;
		case AST_OP_OR: result = make_binop(TAC_OR, code[0]), code[1]); break;
		case AST_OP_G: result = make_binop(TAC_G, code[0]), code[1]); break;
		case AST_OP_L: result = make_binop(TAC_L, code[0]), code[1]); break;
		case AST_DEF_FUNC: result = make_fundef(code[0], code[3]);
		case AST_CMD_FUNCTION_CALL:
		case AST_EXPR_FUNCTION_CALL: result = tac_join(code[0], tac_create(TAC_CALL, 0, (code[0] != NULL) ? code[0]->target, NULL)); break;

		case ASTREE_SCALAR_ASS: result = tac_join(code[0], tac_create(TAC_MOV, node->value, (code[0] != NULL) ? code[0]->target : NULL, 0); break;
		case AST_CMD_IF: result = make_if_then_else(code[0], code[1], code[2]); break;
		case AST_CMD_LOOP: result = make_loop(code[0], code[1]); break;
		default:
			result = tac_join(tac_join(tac_join(code[0], code[1]), code[2]), code[3] );
			break;
	}

	tac_print_list(result);
	return result;
}

TAC* make_unop(int type, TAC* code0) {
	return tac_create(type, makeTemp(&symbolsTable), (code0 != NULL) ? code0->target : NULL, NULL);
}

TAC* make_binop(int type, TAC* code0, TAC* code1) {
	/*
	TAC* result;
	TAC* nova;

	tac_join(code0, code1);	//tac_join() tolerante a ponteiros nulos :)
	nova = tac_create(type , makeTemp(&symbolsTable), (code0 != NULL) ? code0->target : NULL, (code1 != NULL) ? code1->target : NULL);
	nova->prev = result;
	result = nova;
	*/
	return tac_join(tac_join(code0, code1),
		             tac_create(type , makeTemp(&symbolsTable), (code0 != NULL) ? code0->target : NULL, (code1 != NULL) ? code1->target : NULL));
}

/*
TAC* makeIfThen(TAC* code0, TAC* code1) {
	TAC* nova1;
	TAC* nova2;
	row* label;

	nova1 = tac_create(TAC_JZ, label = makeLabel(&symbolsTable), (code0!=NULL)?code0->target:NULL, 0);	//Criar tipo TAC_JZ
	nova2 = tac_create(TAC_LABEL, label, 0, 0);											//Criar tipo TAC_LABEL

	return tac_join(tac_join(tac_join(code0, nova1), code1), nova2);
}
*/

TAC* make_if_then_else(TAC* code0, TAC* code1, TAC* code2) {
	TAC* nova1;
	TAC* nova2;
	TAC* nova3;
	row* labelIFZ;
	row* labelJMP;

	nova1 = tac_create(TAC_IFZ, labelIFZ = makeLabel(&symbolsTable), (code0!=NULL)?code0->target:NULL, 0);	//Criar tipo TAC_JZ
	nova2 = tac_create(TAC_LABEL, labelIFZ, 0, 0);											//Criar tipo TAC_LABEL
	
	if(code2 != NULL) {
		nova3 = tac_create(TAC_JMP, labelJMP = makeLabel(&symbolsTable), 0, 0);
		nova4 = tac_create(TAC_LABEL, labelJMP, 0, 0);
		return tac_join(tac_join(tac_join(tac_join(tac_join(tac_join(code0, nova1), code1), nova2), nova3), code2), nova4); 
	}

	return tac_join(tac_join(tac_join(code0, nova1), code1), nova2);
}

TAC* make_loop(TAC* code0, TAC* code1) {
	TAC* nova1;
	TAC* nova2;
	row* labelIFZ;
	row* labelJMP;

	labelJMP = makeLabel(&symbolsTable);
	labelIFZ = makeLabel(&symbolsTable);
	
	nova1 = tac_create(TAC_LABEL, labelJMP, 0, 0);
	nova2 = tac_create(TAC_IFZ, labelIFZ, (code0!=NULL)?code0->target:0, 0);
	nova3 = tac_create(TAC_JMP, labelJMP, 0, 0);
	nova4 = tac_create(TAC_LABEL, labelIFZ, 0, 0);
 
	return tac_join(tac_join(tac_join(tac_join(tac_join(nova1, code0), nova2), code1), nova3), nova4);
}

/* code0: identificador da funcao, code1: corpo */
TAC* make_fundef(TAC* code0, TAC* code1) {
	row* labelFun;
	TAC* nova1;
	TAC* nova2;
	TAC* nova3;

	labelFun = put(code0->target, SYMBOL_IDENTIFIER, &symbolsTable);
	nova1 = tac_create(TAC_LABEL, labelFun, 0, 0);
	nova2 = tac_create(TAC_BEGINFUN, 0, 0, 0);
	nova3 = tac_create(TAC_ENDFUN, 0, 0, 0);

	return tac_join(tac_join(tac_join(nova1, code1), nova2), nova3);
}
